import socket
import json
import time
import datetime

import hashlib
from threading import *

from myLog import logger


# # 计算CRC校验值
# def calculate_crc(data):
#     """计算CRC校验值"""
#     crc = hashlib.md5(data.encode()).hexdigest()[:4]
#     return crc


class LuxServer():
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port
        self.skt_server = socket.socket()
        self.skt_client = None

        self.thread_end_flag = False

        self.event_L001 = Event() # 线程阻塞标识，用于等待L001消息(心跳请求)的事件
        self.event_L002 = Event()
        self.event_L003 = Event() 
        self.event_L004 = Event() 

        # 全局字典，用于多线程间数据传输，FuncCode做键，收到的内容做值
        # global L001_DUR_dict, L002_DUR_dict, L003_DUR_dict, L004_DUR_dict
        self.L001_dict = {}
        self.L002_dict = {}
        self.L003_dict = {}
        self.L004_dict = {}


    # 启动服务端
    def serverStart(self):
        self.skt_server.bind((self.ip, self.port))
        self.skt_server.listen(5)    # 最多允许一个客户端连接
        print(f"服务端已启动，监听 {self.ip}:{self.port}")

        while True:
            print(f"等待客户端连接...")
            try:
                self.skt_client, client_address = self.skt_server.accept()
                print(f"客户端 {client_address} 已连接")
                # handle_connection(skt_client)
                # skt_client.close()
                return True
            except socket.error as e:
                print(f"接受连接时发生错误: {e}")
                return False


    # 接收客户端消息
    def recvFromClient(self):
        while True:
            if self.thread_end_flag:
                break
            try:
                data = self.skt_client.recv(1024).decode('utf-8')
                # logger.info('recvFromClient() success: {}'.format(data))
            except Exception as e:
                if e.args[0] == 'timed out':
                    # logger.warning("recvFromClient() failed: {}".format(e))
                    continue
                else:
                    # logger.warning("recv() failed: {}".format(e))
                    # time.sleep(0.5)
                    continue  # 断联了，继续接收消息，直到重连成功
            else:
                if data:
                    self.analyze_data(data)
                # else:
                #     logger.warning("The received data is empty.")


    # 对接收的数据进行解析
    def analyze_data(self, data):
        global Event_TID
        data_list = data.split("\r\n")               # 用换行符切分消息字符串，生成list
        for s in data_list:
            if s == "":
                data_list.remove(s)                  # 去除列表里面的空字符串,留下有效数据
                

        for data_unique in data_list:
            try:
                data_unique = data_unique[9:-5]
                # logger.info("data_unique:{}".format(data_unique))
                data_dict = json.loads(data_unique)  # json_str转dict
            except Exception as e:
                logger.error("解析错误：".format(e))
            else:
                funcCode_recv = data_dict.get("funcCode")
                headFlag_recv = data_dict.get("headFlag")
                tid_recv = data_dict.get("tid")
                dataBody_rev = data_dict.get("dataBody")
                # logger.warning("----data----: {}-{}".format(funcCode_recv, headFlag_recv))

                # 把数据解析出来后，根据内容进行处理。可能收到以下三类消息:

                # ---- 心跳请求
                if (funcCode_recv == "L001") & (headFlag_recv == "DU"):
                    self.L001_dict["tid"] = tid_recv
                    self.event_L001.set()

                # --- 客户端对请求下载秘钥事件的应答
                elif (funcCode_recv == "L002") & (headFlag_recv == "TUR"):
                    # self.L002_dict[funcCode_recv] = dataBody_rev
                    self.event_L002.set()

                # # --- 客户端上报下载进度
                elif (funcCode_recv == "L003") & (headFlag_recv == "DU"):
                    self.L003_dict["tid"] = tid_recv
                    self.L003_dict["L003"] = dataBody_rev
                    self.event_L003.set()

                # --- 客户端上报下载结果
                elif (funcCode_recv == "L004") & (headFlag_recv == "DU"):
                    self.L004_dict["tid"] = tid_recv
                    self.L004_dict["L004"] = dataBody_rev
                    self.event_L004.set()


    # 发送数据给客户端
    def sendToClient(self, headFlag, funcCode, tid, dataBody):
        time_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S%f")[2:17]
        DataLen = len(json.dumps(dataBody).replace(' ','')) # int类型，如78
        DataLen = "%04d" % DataLen                          # 格式化成4字符str，如'0078'
        request_data = {"headFlag":headFlag, 
                "time":time_now, 
                "funcCode":funcCode, 
                "version":"V1.0", 
                "tid":tid, 
                "dataLen":DataLen, 
                "dataBody":dataBody,
                }
        request_json = json.dumps(request_data)

        CRC = "A1B2"
        # ---------发数据---------
        str_json = "Lux " + str(len(request_json)).zfill(4) + " " + request_json + " " + CRC + "\r\n"
        try:
            self.skt_client.send(str_json.encode())
        except Exception as e:
            logger.error("sendToClient() failed: {} -- {}".format(funcCode, e))
            return 'Fail'      # send失败通常是IOError，是因为服务端连接断开了
        else:
            return 'Success'   # 只表示发送成功了，对方是否应答成功需要再判断


    # 请求下载秘钥 L002 (在主线程)
    def downloadKey(self, keyType = '1', port = '1'):
        for loop in range(3):
            headFlag = "TU"
            funcCode = "L002"
            tid = str(int(time.time()*1000))
            dataBody = {"keyType" : keyType,
                        "port" : port
                        }

            if self.sendToClient(headFlag, funcCode, tid, dataBody) != 'Success':
                logger.error("请求下载秘钥失败: {}".format(funcCode))
                continue
            self.event_L002.wait(timeout = 5)
            if self.event_L002.is_set():
                print("已发送下载秘钥的请求")
                self.event_L002.clear()
                break


    # 应答客户端的心跳请求
    def heartBeatResponse(self):
        while True:
            if self.thread_end_flag:
                break
            if self.event_L001.is_set():
                self.event_L001.clear()

                headFlag = "DUR"
                funcCode = "L001"
                tid = self.L001_dict.get("tid")
                dataBody = {"resCode": "0"}
                if self.sendToClient(headFlag, funcCode, tid, dataBody) != 'Success':
                    logger.error("心跳应答 失败: {}".format(funcCode))
                    pass
                else:
                    # logger.info("心跳应答 成功: {}".format(funcCode))
                    pass



    # 应答客户端的下载进度
    def downloadProgressResponse(self):
            while True:
                if self.thread_end_flag:
                    break
                if self.event_L003.is_set():
                    self.event_L003.clear()

                    dataBody = self.L003_dict.get("L003")
                    progress = dataBody.get("progress")

                    if progress != "":
                        print("下载进度: " + progress + "%")

                    headFlag = "DUR"
                    funcCode = "L003"
                    tid = self.L003_dict.get("tid")
                    dataBody = {"resCode": "0"}
                    if self.sendToClient(headFlag, funcCode, tid, dataBody) != 'Success':
                        logger.error("下载进度应答 失败: {}".format(funcCode))
                        pass
                    else:
                        # logger.info("下载进度应答 成功: {}".format(funcCode))
                        pass
    

    # 应答客户端的下载结果
    def downloadResultResponse(self):
        while True:
            if self.thread_end_flag:
                break
            if self.event_L004.is_set():
                self.event_L004.clear()

                dataBody = self.L004_dict.get("L004")
                result = dataBody.get("result")

                if result == "1":
                    print("√ √ √ 下载成功")
                else:
                    print("x x x 下载失败")

                headFlag = "DUR"
                funcCode = "L004"
                tid = self.L004_dict.get("tid")
                dataBody = {"resCode": "0"}
                if self.sendToClient(headFlag, funcCode, tid, dataBody) != 'Success':
                    logger.error("应答下载结果 失败: {}".format(funcCode))
                    pass
                else:
                    # logger.info("下载应答结果 成功: {}".format(funcCode))
                    pass



def main():
    print('欢迎进入服务端')
    ip = input("请输入IP: ")
    port = input("请输入端口号:")
    myserver = LuxServer(str(ip), int(port))
    # 启动服务端
    if myserver.serverStart() != False:
        # 接收客户端消息 (子线程)
        t1 = Thread(target = myserver.recvFromClient)
        t1.start()
        # 应答心跳 （子线程）
        t2 = Thread(target = myserver.heartBeatResponse)
        t2.start()
        # 应答下载进度 （子线程）
        t3 = Thread(target = myserver.downloadProgressResponse)
        t3.start()
        # 应答下载结果 （子线程）
        t4 = Thread(target = myserver.downloadResultResponse)
        t4.start()


    else:
        logger.error("serverStart() failed")
        return

    while True:
        
        print('-----------------------')
        print("是否发送请求下载秘钥?")
        cmd = input(" 0-不下载; 1-小米; 2-OPPO; 3-vivo; 4-华为; 5-荣耀; 9-退出: " )
        print('-----------------------')
        if cmd == '1' or cmd == '2' or cmd == '3' or cmd == '4' or cmd == '5':
            # 发送请求下载秘钥
            keyType = cmd
            port = input("请输入端口号(1~8): " )
            if port not in ['1','2','3','4','5','6','7','8'] or port == '':
                print("请输入有效值！")
                port = '1'
            print('-----------------------')
            myserver.downloadKey(keyType, port)
            time.sleep(4)
        elif cmd == '9':
            myserver.thread_end_flag = True
            t1.join()
            t2.join()
            t3.join()
            t4.join()
            break
        elif cmd == '0':
            continue
        else:
            print("请输入有效值！")


if __name__ == "__main__":
    main()
